
/*
 *	EECE 315
 *	Project #2
 *	
 *	A Simple Shell
 *	
 *	Feature 1: Internal Commands
 *	Feature 2: Parallel Process, no waiting to complete process when '&' at end of command
 *	Feature 3: Redirect input or output (using '>')
 *
 *	Authors:
 *			Mohamed Ali
 *			Donald Cheng
 *			Krzysztof Mazurek
 *			Ishan Sahay
 *	
 *	Date: 13 / Oct / 2012
 *	
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "minishell.h"

#define TRUE 1
#define FALSE 0


char *lookupPath(char **, char **); 
int parseCommand(char *, struct command_t*); 
int parsePath(char **); 
void printPrompt(); 
void readCommand(char *); 

int parsePath(char *dirs[]);
int parseCommand(char *cLine, struct command_t *cmd);
char *lookupPath(char **argv, char **dir);

void free_Command( struct command_t * );

int main() 
{
	int i;
	int count = 0;
	pid_t pid;
	char *param[MAX_ARGS];	

	/* Shell initialization */
	
	char  *pathv[MAX_PATH_LEN];
	parsePath(pathv); /* Get directory paths from PATH */
	
	struct command_t command;
	char currentworkingdirectory[50] = "";
	char commandLine[50] = "a";
	long size;
	
	//"Default" values to previous commands from struct command.
	for(int i=0; i< MAX_ARGS; i++)	
		command.argv[i] = "a";
		//command.argc = MAX_ARGS;
	parseCommand(commandLine, &command);

	while (TRUE) 
	{ 	
		do{			
		// free_Command( &command );
			//printf("\n\nLoop Counter = %d", count);
			printPrompt();
			/* Read the command line and parse it */ 
			readCommand(commandLine); 			
			parseCommand(commandLine, &command); 

			i = strcmp(command.name, "") == 0 ? TRUE : FALSE;
		}	
		while(i);
		
		count++;

/*	
		if(command.name == NULL) {
			/* Report error */ 
/*			printf("\nError: CommandName is null\n");
			//goto next;
		}
*/
		
		//printf("\nInternal. %s\t%d\t%s",command.name,command.argc,command.argv[command.argc]);
		
		//Feature 1
		//Internal commands if( strcmp(command.name, "cd" ) == 0)..else if..else if...
		if ( strcmp(command.name, "cd") == 0 ) {
			//Verification of having a second argument for "cd <argument>" command
			if( command.argv[1] != NULL)
			{
				//Special case of navigating to parent directory			
				size = pathconf( ".", 50);
				if ( strcmp( command.argv[1] , "") == 0){
					chdir("..");
				}
				else //Navigating subdiretory(ies).	
				{		
					chdir(command.argv[1]);
					//printf("\nUsing chdir\n");
					//printf("x%sx", command.argv[2]);
				}
			}
			
			//...
			
			//Output to be redirected. (Feature 3)
			
				//goto next;
		}
		else if( strcmp(command.name, "pwd") == 0 ){
			system(command.name);
			
			//Output to be redirected. (Feature 3)
				//goto next;
		}
		else if( strcmp(command.name, "exit") == 0 || strcmp(command.name, "quit") == 0 ){
			printf("\n\nDONE\n");
			//system("exit");	//treats command:exit and command:quit as command:exit.
			break;	//Guarantees to exit while loop so memory deallocation can occur.
		}
	else{
	
		pid = fork();	
		
		//External Commands
		/* Get the full pathname for the file */ 
		command.name = lookupPath(command.argv, pathv);
		
		//Command not found, then (disregard rest of code =>) continue.
		if ( command.name == NULL ) 
			continue;
		
		/* Create child and execute the command */ 
		//...
		/* Wait for the child to terminate */ 
		//...
		
		//printf("\nExternal. %s \t%d\t%s\t",command.name,command.argc,command.argv[command.argc]);
		
		if (pid < 0) 
		{    /* error occurred */
			fprintf(stderr, "\nFork Failed.");
			return 1;
		}
		else if (pid == 0) 
		{     /* child process */
			//printf ("\nChild Executing (%d.)", pid);
			
			if ( strcmp( command.argv[command.argc], "&") == 0 ) //if last argument in commandLine is '&', then NOT Wait for child completion
				command.argv[command.argc] = NULL;
				
				//Original exec() command
				//execlp("/bin/ls", "ls", NULL);
						
			//Required exec() command
			execv(command.name, command.argv);	//"/bin/ls" argv+1 onwards hold the arguments for the specific command issued.
			
			//system("exit");			
		
		//free_Command( &command );			
			//exit(0);
		}
		else 
		{   //printf ("\nParent Executing (%d).", getpid());
			/* parent process */
			if ( strcmp( command.argv[command.argc], "&") == 0 ) //if last argument in commandLine is '&', then NOT Wait for child completion
			{
				/* parent will NOT wait for the child to complete */
				//printf ("\nNot Waiting for Child %d.", pid);
					//goto next;
			}
			else {
				/* parent will wait for the child to complete */
				 
				//printf ("\nWaiting for Child %d...\n...", pid);
				wait (NULL);
				// simulating delay			
				//	for ( int k = 0; k < 1000000000; k++);	//
				//printf ("\nChild Complete."); 
					//goto next;
			}
		}
	
	}
	}
	
	/* Shell termination */
	//...

	//printf("\n%s\t%d\t%s",command.name,command.argc,command.argv[command.argc]);
	
	//printf("\n3. %s\t", command.name);
	
	//Free allocated memory
	free( *pathv );
	// free_Command( &command );
	
	system("exit");
	return 0;
	
}


int parsePath(char *dirs[]) {
	
	/* This function reads the PATH variable for this environment, then builds an array, dirs[], of the directories in PATH */
	
	int i;
	char *dirP;
	
	char *pathEnvVar; char *thePath;
	
	for(i = 0; i < MAX_ARGS; i++) 
		dirs[i] = NULL; 
	
	pathEnvVar = (char *) getenv ("PATH"); 
	thePath = (char *) malloc(strlen(pathEnvVar) + 1); 
	strcpy(thePath, pathEnvVar);
	
	/* Loop to parse thePath. Look for a ':' delimiter between each path name. */ 
	//printf("\n%s", thePath);
	
	dirP = thePath;
	
	dirP = strtok(thePath,":");	
	
	i = 0;
	while (dirP != NULL)
	{
		dirs[i] = dirP;
		//	printf ("\n%s",dirP);
		dirP = strtok (NULL, ":");
		i++;
	}
	
	for (i = 0; dirs[i] != NULL; i++) {
	//printf ("\n%s",dirs[i]);
	}

//Memory deallocation
//..will occur in main() since thePath is being referenced by (and used) a variable in main()	

	thePath = NULL;
	
	return 0;
}


void printPrompt() {
	/* Build the prompt string to have the machine name, current directory, or other desired information */
	
	char promptString[50]; 
	
	int i = 0;
	
	for (i = 0; i < sizeof(promptString); i++) {
		promptString[i] = '1';
	}
	
	//...  
	
	char machine[20] = "Hello\0", directory[15]="";

	printf ("\n%s$%s>>\n", machine, directory) ;
}

void readCommand(char *buffer)
{
	/* This code uses any set of I/O functions, such as those in the stdio library to read the entire command line into the buffer. 
	 * This implementation is greatly simplified, but it does the job. 
	 */
	
	gets(buffer);
	//	fgets(buffer, 100, stdin);
}

int parseCommand(char *cLine, struct command_t *cmd) 
{
	int argc;
	char **clPtr; /* Initialization */ 
	clPtr = &cLine;	/* cLine is the command line */ 
	argc = 0; 
	cmd->argv[argc] = (char *) malloc(MAX_ARG_LEN);
	/* Fill argv[] */ 
	while((cmd->argv[argc] = strsep(clPtr, WHITESPACE)) != NULL)
	{
		cmd->argv[++argc] = (char *) malloc(MAX_ARG_LEN);
	}
	
	/* Set the command name and argc */ 
	cmd->argc = argc - 1;
	cmd->name = (char *) malloc(sizeof(cmd->argv[0])); 
	strcpy(cmd->name, cmd->argv[0]); 
	return 1;
}

void free_Command( struct command_t *cmd )
{
	//Use free(void *ptr) to deallocate the dynamic memory from struct command_t

	int i = 0; //Counter for cmd->argc

	//if(cmd != NULL){
	free(cmd->name);
	cmd->name = NULL;		
		while( cmd->argv[i] != NULL  && i < cmd->argc ){
			free(cmd->argv[i]);
			cmd->argv[i] = NULL;
			i++;		
		}	
	//}
}

char *lookupPath(char **argv, char **dir)
{
	/* This function searches the directories identified by the dir argument to see if argv[0] (the file name) appears there.
	 Allocate a new string, place the full path name in it, then return the string. */
	
	int i;
	
	char *result; 
	char pName[MAX_PATH_LEN];
	
	/* Check to see if file name is already an absolute path name */ 
	if(*argv[0] == '/')
	{
		//...
		result = argv[0];
		return result;
	}
	
	
	/* Look in PATH directories. Use access() to see if the file is in a dirrectory from PATH.*/
	
	for(i = 0; i < MAX_PATHS; i++) 
	{ 
		//...
		if ( dir[i] == NULL ) {
			break;
		}
		
		//Create the full pName to check if path exists.
		strcpy(pName, dir[i]);
		strcat(pName, "/");
		strcat(pName, argv[0]);
		
		//Using command: int access(const char *path, int amode), to determine if command exists in PATH.
		//F_OK:	Test of file existence of 'path'
		//X_OK:	Test for execute permission of 'path' 
		if( access(pName, F_OK) == 0 )
		{
			result = pName;
			return result;
		}
	} 
	
	/* if file name not found in any path variable till now then*/ 
	fprintf(stderr, "\n%s: command not found", argv[0]); 
	return NULL;	
}

